package yawning.mew.cat;

import yawning.mew.ProcEffect.Trigger;
import yawning.mew.cat.CatModelSimState.CatTargetData;
import yawning.mew.character.CharacterModel.AttackResult;
import yawning.mew.sim.SimulationAction;
import yawning.mew.sim.SimulationEventAction;
import yawning.mew.sim.SimulationTarget;

public class EventRake extends SimulationEventAction {
	CatModelSimState mState;
	private SimulationTarget mTarget;
	private String mTargetName;
	private CatTargetData mTargetData;
	private double mAttackPower;
	private double mPCrit;
	private double mMasteryMult;
	private double mToTTMult;
	boolean mTigersFury;

	EventRake(CatModelSimState state, CatTargetData targetData) {
		super(state.mSimEngine);
		mState = state;
		mTarget = targetData.getSimulationTarget();
		mTargetName = mTarget.getTarget().getName();
		mTargetData = targetData;
	}

	@Override
	public double perform() {
		int cost = mState.getEnergyCost(mState.mModel.rakeCost);
		if (mState.getEnergy() < cost && !mState.mOocEv.isUp()) return SimulationAction.ACTION_FAIL;
		int prevEnergy = mState.getEnergy(); int prevCps = mState.getComboPoints();

		AttackResult result = (mTargetData.mCanParry) ? mState.mModel.getFrontalYellowResult() : mState.mModel.getYellowResult();

		if (result == AttackResult.MISS) {
			cost = (int) Math.round(cost * 0.2);
			mNrRakeMiss++;

			if (mState.mIsLogging) mState.mLog.log("Rake: %s MISS\n", mTargetName);
		} else if (result == AttackResult.DODGE) {
			cost = (int) Math.round(cost * 0.2);
			mNrRakeDodge++;

			if (mState.mIsLogging) mState.mLog.log("Rake: %s DODGE\n", mTargetName);
		} else if (result == AttackResult.PARRY) {
			cost = (int) Math.round(cost * 0.2);
			mNrRakeParry++;

			if (mState.mIsLogging) mState.mLog.log("Rake: %s PARRY\n", mTargetName);
		} else {
			double damage = mState.mModel.levelDep.getRakeDmg() + mState.mModel.getAttackPower() * mState.mModel.levelDep.getRakeDmgCoeff();

			if (result == AttackResult.CRIT) {
				mNrRakeCrit++;
				damage *= mState.mModel.multCrit;
			} else {
				mNrRakeHit++;
			}

			mMasteryMult = mState.mModel.getBleedMastery();
			mToTTMult = mState.mTottEv.getMult();

			damage *= mState.getBleedDamageMult(mMasteryMult, mTargetData);
			damage *= mState.mModel.tier_10_4pc_rakeMult;
			damage *= mState.mTigersFuryEv.isUp() ? mState.mModel.multTigersFury : 1.0;
			damage *= mToTTMult;
			mRakeDamage += Math.round(damage);

			if (mState.mIsLogging) mState.mLog.log("Rake: %s %s Damage: %d\n", mTargetName, result, Math.round(damage));

			mAttackPower = mState.mModel.getAttackPower();
			mPCrit = mState.mModel.getMeleeCritChance();
			mTigersFury = mState.mTigersFuryEv.isUp();

			if (!isUp())
				schedule(mState.mModel.getRakeDebuffDuration(), mState.mModel.getRakeTickInterval());
			else
				extendExpiry(mState.mModel.getRakeDebuffDuration() + getNextTickTime() - getExpiryTime());

			if (result == AttackResult.CRIT) mState.mSimEngine.triggerProc(mTarget, Trigger.YELLOWCRIT); else mState.mSimEngine.triggerProc(mTarget, Trigger.YELLOW);

			mState.onCpGenerator(result == AttackResult.CRIT);
		}

		if (mState.mTigersFuryEv.isUp()) mNrRakeTF++;
		if (mState.mOocEv.isUp()) mNrRakeOoC++;
		mRakeEnergy += mState.onOoCConsumer(cost);

		mState.logResourceChange(prevEnergy, prevCps);

		return 1.0;
	}

	@Override
	public void onTick() {
		double damage = mState.mModel.levelDep.getRakeDotDmg() + mAttackPower * mState.mModel.levelDep.getRakeDotCoeff();
		AttackResult result = mState.mModel.getDotResult(mPCrit);

		if (result == AttackResult.CRIT) {
			mNrRakeDotCrit++;
			damage *= mState.mModel.multCrit;
		} else {
			mNrRakeDotHit++;
		}

		damage *= mState.getBleedDamageMult(mMasteryMult, mTargetData);
		damage *= mState.mModel.tier_10_4pc_rakeMult;
		damage *= mState.mModel.tier_11_2pc_rakeMult;
		damage *= mTigersFury ? mState.mModel.multTigersFury : 1.0;
		damage *= mToTTMult;
		if (mTigersFury) mNrRakeDotTFTicks++;
		mRakeDotDamage += Math.round(damage);

		if (mState.mIsLogging) mState.mLog.log("Rake Tick: %s %s Damage: %d\n", mTargetName, result, Math.round(damage));

		if (result == AttackResult.CRIT) mState.mSimEngine.triggerProc(mTarget, Trigger.DOTCRIT); else mState.mSimEngine.triggerProc(mTarget, Trigger.DOT);
	}

	@Override
	public void onExpire() {
		if (mState.mIsLogging) mState.mLog.log("Rake: %s Expiration\n", mTargetName);
	}

	long mRakeDamage = 0;
	int mNrRakeMiss = 0;
	int mNrRakeDodge = 0;
	int mNrRakeParry = 0;
	int mNrRakeCrit = 0;
	int mNrRakeHit = 0;

	long mRakeDotDamage = 0;
	int mNrRakeDotCrit = 0;
	int mNrRakeDotHit = 0;
	int mNrRakeDotTFTicks = 0;

	int mNrRakeTF;
	int mRakeEnergy;
	int mNrRakeOoC;
}